//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file
///
///
/// 
/// @author Davide Pasca
/// 
//==================================================================

#include "stdafx.h"
#include "RendDevice.h"

#define USE_AA

//==================================================================

//--------------------------------------------------------------------------------------
// Name: struct TILING_SCENARIO
// Desc: Describes a configuration of MSAA quality and tiling rectangles to be used for
//       Predicated Tiling.
//--------------------------------------------------------------------------------------
struct TILING_SCENARIO
{
	DWORD                   dwScreenWidth;
	DWORD                   dwScreenHeight;
	D3DMULTISAMPLE_TYPE     MSAAType;
	DWORD                   dwTileCount;
	D3DRECT                 TilingRects[15];
};

//==================================================================
enum {
	TILING_SCEN_2xMSAA_2H,
	TILING_SCEN_2xMSAA_2V,
	TILING_SCEN_2xMSAA_3H,
};
//==================================================================
// 1280x720 tiling scenarios
static const TILING_SCENARIO _tiling_scenarios720[] =
{
    {
        //L"1280x720 2xMSAA Horizontal Split",
        1280,
        720,
        D3DMULTISAMPLE_2_SAMPLES,
        2,
        {
            { 0,   0, 1280, 384 },
            { 0, 384, 1280, 720 }
        }
    },
    {
        //L"1280x720 2xMSAA Vertical Split",
        1280,
        720,
        D3DMULTISAMPLE_2_SAMPLES,
        2,
        {
            {   0, 0,  640, 720 },
            { 640, 0, 1280, 720 }
        }
    },
    {
        //L"1280x720 4xMSAA Horizontal Split",
        1280,
        720,
        D3DMULTISAMPLE_4_SAMPLES,
        3,
        {
            { 0,   0, 1280, 256 },
            { 0, 256, 1280, 512 },
            { 0, 512, 1280, 720 },
        }
    },
    { NULL }
};

static const TILING_SCENARIO *_cur_scenariop = &_tiling_scenarios720[ TILING_SCEN_2xMSAA_3H ];

//--------------------------------------------------------------------------------------
// Name: LargestTileRectSize()
// Desc: Returns the dimensions that fit the largest rectangle(s) out of the tiling
//       rectangles.
//--------------------------------------------------------------------------------------
static void largestTileRectSize( const TILING_SCENARIO& Scenario, D3DPOINT* pMaxSize )
{
    pMaxSize->x = 0;
    pMaxSize->y = 0;
    for( DWORD i = 0; i < Scenario.dwTileCount; i++ )
    {
        DWORD dwWidth = Scenario.TilingRects[i].x2 - Scenario.TilingRects[i].x1;
        DWORD dwHeight = Scenario.TilingRects[i].y2 - Scenario.TilingRects[i].y1;
        if( dwWidth > (DWORD)pMaxSize->x )
            pMaxSize->x = dwWidth;
        if( dwHeight > (DWORD)pMaxSize->y )
            pMaxSize->y = dwHeight;
    }
}

//==================================================================
static int calcTilingRects( D3DRECT tiling_rects[8],
						    int *out_tile_wdp,
							int	*out_tile_hep,
							int total_wd, int total_he,
						    D3DFORMAT buff_format,
							D3DMULTISAMPLE_TYPE msaa_type )
{
	for (int n_tiles=1; n_tiles <= 8; ++n_tiles)
	{
		int proposed_tile_wd = total_wd;
		int proposed_tile_he = total_he;

		bool	is_v_split;

		if NOT( proposed_tile_wd % n_tiles )
		{
			proposed_tile_wd /= n_tiles;
			is_v_split = true;
		}
		else
		{
			if NOT( proposed_tile_he % n_tiles )
			{
				proposed_tile_he /= n_tiles;
				is_v_split = false;
			}
			else
				continue;
		}

		int	tile_wd = XGNextMultiple( proposed_tile_wd, GPU_TEXTURE_TILE_DIMENSION );
		int	tile_he = XGNextMultiple( proposed_tile_he, GPU_TEXTURE_TILE_DIMENSION );

		u_int n_BackBufferEDRAMTiles = XGSurfaceSize( tile_wd, tile_he, buff_format, msaa_type );
		u_int n_ZBufferEDRAMTiles    = XGSurfaceSize( tile_wd, tile_he, D3DFMT_D24S8, msaa_type );

		if ( n_BackBufferEDRAMTiles + n_ZBufferEDRAMTiles <= GPU_EDRAM_TILES )
		{
			for (int i=0; i < n_tiles; ++i)
			{
				if ( is_v_split )
				{
					tiling_rects[i].x1 = (i+0) * tile_wd;
					tiling_rects[i].x2 = (i+1) * tile_wd;
					tiling_rects[i].y1 = 0;
					tiling_rects[i].y2 = total_he;

					tiling_rects[i].x2 = KMIN( tiling_rects[i].x2, total_wd );
				}
				else
				{
					tiling_rects[i].x1 = 0;
					tiling_rects[i].x2 = total_wd;
					tiling_rects[i].y1 = (i+0) * tile_he;
					tiling_rects[i].y2 = (i+1) * tile_he;

					tiling_rects[i].y2 = KMIN( tiling_rects[i].y2, total_he );
				}
			}

			*out_tile_wdp = tile_wd;
			*out_tile_hep = tile_he;

			return n_tiles;
		}
	}

	throw "Too many tiles !!!";

	return 0;
}

//==================================================================
void RendDevice::setupXBOXPredicatedTiling( int frbuff_wd, int frbuff_he )
{
	HRESULT	hr = 0;

	_d3ddevp->CreateTexture(	frbuff_wd, frbuff_he, 1, 0,
								D3DFMT_LE_X8R8G8B8, D3DPOOL_DEFAULT,
								&_front_buff_texturep, NULL );
	if ERR_NULL( _front_buff_texturep )
		throw "Failed to create _front_buff_texturep";

	// Create resolve target textures.
	_d3ddevp->CreateTexture(	frbuff_wd, frbuff_he,
								1, 0, D3DFMT_A16B16G16R16F, D3DPOOL_DEFAULT,
								&_resolve_texturep, NULL );

	int tile_wd;
	int tile_he;

	D3DMULTISAMPLE_TYPE	MSAA_type = D3DMULTISAMPLE_4_SAMPLES;

	_n_tiles = calcTilingRects( _tiling_rects, &tile_wd, &tile_he,
								frbuff_wd, frbuff_he,
								D3DFMT_A2B10G10R10F_EDRAM,//D3DFMT_LE_X8R8G8B8,//D3DFMT_A16B16G16R16_EDRAM
								MSAA_type );

	// Use custom EDRAM allocation to create the render-targets.
	// The color render-target is placed at address 0 in EDRAM.
	D3DSURFACE_PARAMETERS tile_surf_params = {0};
	tile_surf_params.Base = 0;
	hr = _d3ddevp->CreateRenderTarget(	tile_wd, tile_he,
										D3DFMT_A2B10G10R10F_EDRAM,//D3DFMT_A8R8G8B8,//D3DFMT_A16B16G16R16_EDRAM
										MSAA_type, 0, FALSE,
										&_tiling_rendtarget_surfp, &tile_surf_params );
	if ERR_ERROR( hr )
		throw "Failed to create the render target";

	// Record the size of the created render-target, and then set up allocation
	// for the next render-target right after the end of the first render-target.
	// Put the hierarchical Z buffer at the start of hierarchical Z memory.
	tile_surf_params.Base = _tiling_rendtarget_surfp->Size / GPU_EDRAM_TILE_SIZE;
	tile_surf_params.HierarchicalZBase = 0;

	hr = _d3ddevp->CreateDepthStencilSurface(	tile_wd, tile_he, D3DFMT_D24S8,
												MSAA_type, 0, FALSE,
												&_tiling_depthstencil_surfp, &tile_surf_params );
	if ERR_ERROR( hr )
		throw "Failed to create the depth stencil surface";

	_d3ddevp->SetRenderState( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );


	// Create a full screen size render target for producing the final image.  This
	// render target will not be used with predicated tiling, and does not require
	// hardware MSAA.
	tile_surf_params.Base = 0;
	tile_surf_params.HierarchicalZBase = 0;
	hr = _d3ddevp->CreateRenderTarget(	frbuff_wd, frbuff_he,
										D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE,
										0, FALSE, &_final_rendtarget_surfp,
										&tile_surf_params );
	if ERR_ERROR( hr )
		throw "Failed to create _final_rendtarget_surfp";

	//--------------------------------------------------------
	hr = _d3ddevp->CreateTexture(	frbuff_wd / 4, frbuff_he / 4,
									1, D3DUSAGE_RENDERTARGET,
									D3DFMT_A8R8G8B8,
									D3DPOOL_DEFAULT,
									&_pproc_downsample4x4_texturep, NULL );
	if ERR_ERROR( hr )
		throw "Failed to create _pproc_downsample4x4_texturep";

	hr = _d3ddevp->CreateTexture(	frbuff_wd / 8, frbuff_he / 8,
									1, D3DUSAGE_RENDERTARGET,
									D3DFMT_A8R8G8B8,
									D3DPOOL_DEFAULT,
									&_pproc_downsample8x8_texturep, NULL );
	if ERR_ERROR( hr )
		throw "Failed to create _pproc_downsample8x8_texturep";
	

	hr = _d3ddevp->CreateTexture(	frbuff_wd / 8, frbuff_he / 8,
									1, D3DUSAGE_RENDERTARGET,
									D3DFMT_A8R8G8B8,
									D3DPOOL_DEFAULT,
									&_pproc_bloom_texture1p, NULL );
	if ERR_ERROR( hr )
		throw "Failed to create _pproc_bloom_texture1p";

	hr = _d3ddevp->CreateTexture(	frbuff_wd / 8, frbuff_he / 8,
									1, D3DUSAGE_RENDERTARGET,
									D3DFMT_A8R8G8B8,
									D3DPOOL_DEFAULT,
									&_pproc_bloom_texture2p, NULL );
	if ERR_ERROR( hr )
		throw "Failed to create _pproc_bloom_texture2p";
}

//==================================================================
void RendDevice::createRendTargetD3D( RendSystemContext *contextp, int w, int h )
{
	_cur_buff_idx = 0;

	HRESULT	hr = 0;

	// Set up the structure used to create the D3DDevice.
	D3DPRESENT_PARAMETERS d3dpp = {0};

	// force to HDTV 720
	d3dpp.BackBufferWidth		= w;
	d3dpp.BackBufferHeight		= h;
	d3dpp.MultiSampleType		= D3DMULTISAMPLE_NONE;

	d3dpp.BackBufferFormat		 = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferCount		 = 1;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	d3dpp.SwapEffect			 = D3DSWAPEFFECT_DISCARD;	// no tiled - 0
	d3dpp.PresentationInterval	 = D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;	// no tiled - D3DPRESENT_INTERVAL_ONE;

	// Create the Direct3D device.
	if ( FAILED( contextp->CreateDevice( 0, D3DDEVTYPE_HAL, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &_d3ddevp ) ) )
	{
		throw "Failed to create the device";
	}

#ifdef USE_AA
	setupXBOXPredicatedTiling( d3dpp.BackBufferWidth, d3dpp.BackBufferHeight );
#endif	// AA
}

//==================================================================
static void drawFullScreenQuad( D3DDevice *d3ddevp )
{
	Vector4	tverts[4];
	tverts[0].Set( -1,  1, 0 );
	tverts[1].Set(  1,  1, 0 );
	tverts[2].Set(  1, -1, 0 );
	tverts[3].Set( -1, -1, 0 );
	d3ddevp->DrawPrimitiveUP( D3DPT_QUADLIST, 1, (u_char *)tverts, sizeof(tverts[0]) );
}

//==================================================================
void RendDevice::renderTextureToFinalSimple( D3DTexture *src_texp )
{
	_shaders.SelectStdVDef( false, false, false );
	_shaders.SelectPlainTextureShader();

	_d3ddevp->SetTexture( 0, src_texp );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_NEAREST );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_WRAP );

	drawFullScreenQuad( _d3ddevp );
}

//==================================================================
void RendDevice::renderTextureToFinalBloom( D3DTexture *src_base_texp, D3DTexture *src_bloom_texp )
{
	static Vector4 bloom_scale( 0.5f, 0.5f, 0.5f, 0 );
	_shaders.SelectHDRFinalPassShader( bloom_scale );

	_d3ddevp->SetTexture( 0, src_base_texp );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_NEAREST );

	_d3ddevp->SetTexture( 1, src_bloom_texp );
	_state.SetTextureFilter( 1, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_LINEAR );
	_state.SetTextureFilter( 1, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_LINEAR );
	_state.SetTextureFilter( 1, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_LINEAR );
	for (int i=0; i < 2; ++i)
	{
		_d3ddevp->SetSamplerState( i, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP );
		_d3ddevp->SetSamplerState( i, D3DSAMP_ADDRESSV,  D3DTADDRESS_WRAP );
	}

	_shaders.SelectStdVDef( false, false, false );
	drawFullScreenQuad( _d3ddevp );
}

//-----------------------------------------------------------------------------
// Name: CreateRenderTarget()
// Desc: Helper function to set a render target for a texture
//-----------------------------------------------------------------------------
static D3DSurface *CreateRenderTarget(	D3DDevice	*d3ddevp,
										const LPDIRECT3DTEXTURE9 pTexture,
										D3DSURFACE_PARAMETERS* pSurfaceParams = NULL )
{
	XGTEXTURE_DESC Desc;
	XGGetTextureDesc( pTexture, 0, &Desc );

	static D3DSURFACE_PARAMETERS SurfaceParams = {0};
	if( NULL == pSurfaceParams )
		pSurfaceParams = &SurfaceParams;

	// Some texture formats can't be used as render target formats.  Special
	// case the ones we use
	if (Desc.Format == D3DFMT_A16B16G16R16F_EXPAND)
		Desc.Format = D3DFMT_A16B16G16R16F;

	LPDIRECT3DSURFACE9 pRenderTarget;
	d3ddevp->CreateRenderTarget( Desc.Width, Desc.Height, Desc.Format,
		D3DMULTISAMPLE_NONE, 0, FALSE,
		&pRenderTarget, pSurfaceParams );
	return pRenderTarget;
}

//==================================================================
void RendDevice::beginPostProcessing()
{
	_state.SetZTest( false );
	_state.SetZWrite( false );
	_state.SetAlphaTest( false );
	_state.SetBlending( false );
	_state.SetCulling( STDGFX::CULL_OFF );
	_d3ddevp->SetDepthStencilSurface( NULL );
}

//==================================================================
void RendDevice::renderDS4x4Texture( D3DTexture *desp, D3DTexture *srcp, u_int src_w, u_int src_h )
{
	_shaders.SelectDownsample4x4ClampShader( src_w, src_h );
	_d3ddevp->SetTexture( 0, srcp );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_NEAREST );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_CLAMP );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_CLAMP );

	D3DSurface	*des_surfp = CreateRenderTarget( _d3ddevp, desp );
	_d3ddevp->SetRenderTarget( 0, des_surfp );
	_d3ddevp->SetRenderTarget( 1, NULL );
	_d3ddevp->SetRenderTarget( 2, NULL );
	_d3ddevp->SetRenderTarget( 3, NULL );

	_shaders.SelectStdVDef( false, false, false );

	drawFullScreenQuad( _d3ddevp );

	_d3ddevp->Resolve( D3DRESOLVE_RENDERTARGET0 |
					   D3DRESOLVE_CLEARRENDERTARGET,
					   NULL, desp, NULL, 0, 0, NULL, 1.0f, 0L, NULL );

	_d3ddevp->SetRenderTarget( 0, NULL );
	des_surfp->Release();
}

//==================================================================
void RendDevice::renderDS2x2Texture( D3DTexture *desp, D3DTexture *srcp, u_int src_w, u_int src_h )
{
	_shaders.SelectPlainTextureShader();
	_d3ddevp->SetTexture( 0, srcp );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_LINEAR );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_LINEAR );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_LINEAR );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_CLAMP );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_CLAMP );

	D3DSurface	*des_surfp = CreateRenderTarget( _d3ddevp, desp );
	_d3ddevp->SetRenderTarget( 0, des_surfp );
	_d3ddevp->SetRenderTarget( 1, NULL );
	_d3ddevp->SetRenderTarget( 2, NULL );
	_d3ddevp->SetRenderTarget( 3, NULL );

	_shaders.SelectStdVDef( false, false, false );

	drawFullScreenQuad( _d3ddevp );

	_d3ddevp->Resolve(	D3DRESOLVE_RENDERTARGET0 |
						D3DRESOLVE_CLEARRENDERTARGET,
						NULL, desp, NULL, 0, 0, NULL, 1.0f, 0L, NULL );

	_d3ddevp->SetRenderTarget( 0, NULL );
	des_surfp->Release();
}

//==================================================================
void RendDevice::renderBloomTexture(	D3DTexture *desp,
										D3DTexture *srcp,
										u_int src_w, u_int src_h, bool is_horiz,
										float brightness,
										float bright_threshold )
{
	D3DSurface	*des_surfp = CreateRenderTarget( _d3ddevp, desp );

	_d3ddevp->SetTexture( 0, srcp );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIN, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MAX, STDGFX::TEXFILTERMODE_NEAREST );
	_state.SetTextureFilter( 0, STDGFX::TEXFILTERTYPE_MIP, STDGFX::TEXFILTERMODE_NEAREST );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSU,  D3DTADDRESS_CLAMP );
	_d3ddevp->SetSamplerState( 0, D3DSAMP_ADDRESSV,  D3DTADDRESS_CLAMP );

	_d3ddevp->SetRenderTarget( 0, des_surfp );
	_d3ddevp->SetRenderTarget( 1, NULL );
	_d3ddevp->SetRenderTarget( 2, NULL );
	_d3ddevp->SetRenderTarget( 3, NULL );

	Vector4	samp_offs[16];
	Vector4	samp_weis[16];

	static float	fSize = 3;
	//static float	fBrightness = 2;

	if ( is_horiz )
		RendShaders::MakeBloomParams_s( src_w, src_h, 0.0f*M_PI_2, samp_offs, samp_weis, fSize, brightness );
	else
		RendShaders::MakeBloomParams_s( src_w, src_h, 1.0f*M_PI_2, samp_offs, samp_weis, fSize, brightness );

	_shaders.SelectBloomShader( samp_offs, samp_weis, bright_threshold );
           
    drawFullScreenQuad( _d3ddevp );

	_d3ddevp->Resolve( D3DRESOLVE_RENDERTARGET0 |
					   D3DRESOLVE_CLEARRENDERTARGET,
					   NULL, desp, NULL, 0, 0, NULL, 1.0f, 0L, NULL );

	_d3ddevp->SetRenderTarget( 0, NULL );
	des_surfp->Release();
}

//==================================================================
void RendDevice::renderBeginD3D()
{
#ifdef USE_AA
	_d3ddevp->SetRenderTarget( 0, _tiling_rendtarget_surfp );
	_d3ddevp->SetDepthStencilSurface( _tiling_depthstencil_surfp );

	const static D3DVECTOR4 clear_color = { 0, 0, 0, 0 };
	// Begin tiling.  We specify the tile count, the tiling rectangles, and the
	// color and Z/stencil values to clear the tiling render-target with for each tile.
	_d3ddevp->BeginTiling(	0, _n_tiles, _tiling_rects, &clear_color, 1.0f, 0L );

	// Begin Z pass.  The Z pass is a subset of the full scene rendering.
	//	_d3ddevp->BeginZPass( 0 );
#else
	_d3ddevp->Clear( 0L, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,
						D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0L );
#endif

	_d3ddevp->SetRenderState( D3DRS_HALFPIXELOFFSET, TRUE );
}

//==================================================================
void RendDevice::renderEndD3DSwap()
{
#ifdef USE_AA
	// End Z pass.
//		_d3ddevp->EndZPass();

	const static D3DVECTOR4 clear_color = { 0, 0, 0, 0 };
	// End tiling.  This will cause the accumulated command buffer to be replayed
	// for each tile, predicated upon the boundaries of each tile.  At the end of
	// each tile's rendering, it will be resolved into a section of the front buffer
	// texture.
	_d3ddevp->EndTiling(	D3DRESOLVE_ALLFRAGMENTS | 
							D3DRESOLVE_CLEARRENDERTARGET | 
							D3DRESOLVE_CLEARDEPTHSTENCIL, 
							NULL,
							_resolve_texturep,
							&clear_color,
							1.0f,
							0L,
							NULL );

	beginPostProcessing();

	if ( _bloom_onoff )
	{
		renderDS4x4Texture( _pproc_downsample4x4_texturep, _resolve_texturep, _disp_w, _disp_h );
		renderDS2x2Texture( _pproc_downsample8x8_texturep, _pproc_downsample4x4_texturep, _disp_w/4, _disp_h/4 );
		renderBloomTexture( _pproc_bloom_texture1p,	_pproc_downsample8x8_texturep,	_disp_w/8, _disp_h/8, false, _bloom_brightness, _bloom_bright_threshold );
		renderBloomTexture( _pproc_bloom_texture2p,	_pproc_bloom_texture1p,			_disp_w/8, _disp_h/8, true, _bloom_brightness, _bloom_bright_threshold );

		_d3ddevp->SetRenderTarget( 0, _final_rendtarget_surfp );
		_d3ddevp->SetRenderTarget( 1, NULL );
		_d3ddevp->SetRenderTarget( 2, NULL );
		_d3ddevp->SetRenderTarget( 3, NULL );

		// blit resolved textures to current target
		renderTextureToFinalBloom( _resolve_texturep, _pproc_bloom_texture2p );
	}
	else
	{
		_d3ddevp->SetRenderTarget( 0, _final_rendtarget_surfp );
		_d3ddevp->SetRenderTarget( 1, NULL );
		_d3ddevp->SetRenderTarget( 2, NULL );
		_d3ddevp->SetRenderTarget( 3, NULL );

		// blit resolved textures to current target
		renderTextureToFinalSimple( _resolve_texturep );
	}

	// Swap to the current front buffer, so we can see it on screen.
	_d3ddevp->SynchronizeToPresentationInterval();

	// Resolve the final image to the front buffer.
	_d3ddevp->Resolve(	D3DRESOLVE_RENDERTARGET0, 
						NULL, 
						_front_buff_texturep, 
						NULL, 0, 0,
						&clear_color, 1.0f, 0, NULL );

	_d3ddevp->Swap( _front_buff_texturep, NULL );

	// Swap buffer usage
	_cur_buff_idx = _cur_buff_idx ? 0L : 1L;
#else
	_d3ddevp->Present( NULL, NULL, NULL, NULL );
#endif

_d3ddevp->SetStreamSource( 0, NULL, 0, 0 );
}
